
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       rc_channel.h
  * @author     baiyang
  * @date       2022-11-16
  ******************************************************************************
  */

#pragma once

#ifdef __cplusplus
extern "C"{
#endif

/*----------------------------------include-----------------------------------*/
#include <stdint.h>
#include <stdbool.h>

#include "rc.h"

#include <parameter/param.h>
#include <common/gp_defines.h>
/*-----------------------------------macro------------------------------------*/
#ifndef RC_INPUT_MAX_CHANNELS
#define NUM_RC_CHANNELS 16
#else
#define NUM_RC_CHANNELS RC_INPUT_MAX_CHANNELS
#endif


#define  RC_MIN_LIMIT_PWM     800
// pwm value above which we consider that Radio value is invalid
#define  RC_MAX_LIMIT_PWM     2200

// pwm value above which we condider that Radio min value is invalid
#define  RC_CALIB_MIN_LIMIT_PWM       1300
// pwm value under which we condider that Radio max value is invalid
#define  RC_CALIB_MAX_LIMIT_PWM       1700

// pwm value above which the switch/button will be invoked:
#define  AUX_SWITCH_PWM_TRIGGER_HIGH  1800
// pwm value below which the switch/button will be disabled:
#define  AUX_SWITCH_PWM_TRIGGER_LOW   1200

// pwm value above which the option will be invoked:
#define AUX_PWM_TRIGGER_HIGH  1700
// pwm value below which the option will be disabled:
#define AUX_PWM_TRIGGER_LOW   1300
/*----------------------------------typedef-----------------------------------*/
enum RcControlType {
    RC_CONTROL_TYPE_ANGLE = 0,
    RC_CONTROL_TYPE_RANGE = 1,
};

// Aux Switch enumeration
enum RC_AUX_FUNC {
    RC_AUX_DO_NOTHING =           0, // aux switch disabled
    RC_AUX_FLIP =                 2, // flip
    RC_AUX_SIMPLE_MODE =          3, // change to simple mode
    RC_AUX_RTL =                  4, // change to RTL flight mode
    RC_AUX_SAVE_TRIM =            5, // save current position as level
    RC_AUX_SAVE_WP =              7, // save mission waypoint or RTL if in auto mode
    RC_AUX_CAMERA_TRIGGER =       9, // trigger camera servo or relay
    RC_AUX_RANGEFINDER =         10, // allow enabling or disabling rangefinder in flight which helps avoid surface tracking when you are far above the ground
    RC_AUX_FENCE =               11, // allow enabling or disabling fence in flight
    RC_AUX_RESETTOARMEDYAW =     12, // UNUSED
    RC_AUX_SUPERSIMPLE_MODE =    13, // change to simple mode in middle, super simple at top
    RC_AUX_ACRO_TRAINER =        14, // low = disabled, middle = leveled, high = leveled and limited
    RC_AUX_SPRAYER =             15, // enable/disable the crop sprayer
    RC_AUX_AUTO =                16, // change to auto flight mode
    RC_AUX_AUTOTUNE =            17, // auto tune
    RC_AUX_LAND =                18, // change to LAND flight mode
    RC_AUX_GRIPPER =             19, // Operate cargo grippers low=off, middle=neutral, high=on
    RC_AUX_PARACHUTE_ENABLE  =   21, // Parachute enable/disable
    RC_AUX_PARACHUTE_RELEASE =   22, // Parachute release
    RC_AUX_PARACHUTE_3POS =      23, // Parachute disable, enable, release with 3 position switch
    RC_AUX_MISSION_RESET =       24, // Reset auto mission to start from first command
    RC_AUX_ATTCON_FEEDFWD =      25, // enable/disable the roll and pitch rate feed forward
    RC_AUX_ATTCON_ACCEL_LIM =    26, // enable/disable the roll, pitch and yaw accel limiting
    RC_AUX_RETRACT_MOUNT1 =      27, // Retract Mount1
    RC_AUX_RELAY =               28, // Relay pin on/off (only supports first relay)
    RC_AUX_LANDING_GEAR =        29, // Landing gear controller
    RC_AUX_LOST_VEHICLE_SOUND =  30, // Play lost vehicle sound
    RC_AUX_MOTOR_ESTOP =         31, // Emergency Stop Switch
    RC_AUX_MOTOR_INTERLOCK =     32, // Motor On/Off switch
    RC_AUX_BRAKE =               33, // Brake flight mode
    RC_AUX_RELAY2 =              34, // Relay2 pin on/off
    RC_AUX_RELAY3 =              35, // Relay3 pin on/off
    RC_AUX_RELAY4 =              36, // Relay4 pin on/off
    RC_AUX_THROW =               37, // change to THROW flight mode
    RC_AUX_AVOID_ADSB =          38, // enable AP_Avoidance library
    RC_AUX_PRECISION_LOITER =    39, // enable precision loiter
    RC_AUX_AVOID_PROXIMITY =     40, // enable object avoidance using proximity sensors (ie. horizontal lidar)
    RC_AUX_ARMDISARM_UNUSED =    41, // UNUSED
    RC_AUX_SMART_RTL =           42, // change to SmartRTL flight mode
    RC_AUX_INVERTED  =           43, // enable inverted flight
    RC_AUX_WINCH_ENABLE =        44, // winch enable/disable
    RC_AUX_WINCH_CONTROL =       45, // winch control
    RC_AUX_RC_OVERRIDE_ENABLE =  46, // enable RC Override
    RC_AUX_USER_FUNC1 =          47, // user function #1
    RC_AUX_USER_FUNC2 =          48, // user function #2
    RC_AUX_USER_FUNC3 =          49, // user function #3
    RC_AUX_LEARN_CRUISE =        50, // learn cruise throttle (Rover)
    RC_AUX_MANUAL       =        51, // manual mode
    RC_AUX_ACRO         =        52, // acro mode
    RC_AUX_STEERING     =        53, // steering mode
    RC_AUX_HOLD         =        54, // hold mode
    RC_AUX_GUIDED       =        55, // guided mode
    RC_AUX_LOITER       =        56, // loiter mode
    RC_AUX_FOLLOW       =        57, // follow mode
    RC_AUX_CLEAR_WP     =        58, // clear waypoints
    RC_AUX_SIMPLE       =        59, // simple mode
    RC_AUX_ZIGZAG       =        60, // zigzag mode
    RC_AUX_ZIGZAG_SaveWP =       61, // zigzag save waypoint
    RC_AUX_COMPASS_LEARN =       62, // learn compass offsets
    RC_AUX_SAILBOAT_TACK =       63, // rover sailboat tack
    RC_AUX_REVERSE_THROTTLE =    64, // reverse throttle input
    RC_AUX_GPS_DISABLE  =        65, // disable GPS for testing
    RC_AUX_RELAY5 =              66, // Relay5 pin on/off
    RC_AUX_RELAY6 =              67, // Relay6 pin on/off
    RC_AUX_STABILIZE =           68, // stabilize mode
    RC_AUX_POSHOLD   =           69, // poshold mode
    RC_AUX_ALTHOLD   =           70, // althold mode
    RC_AUX_FLOWHOLD  =           71, // flowhold mode
    RC_AUX_CIRCLE    =           72, // circle mode
    RC_AUX_DRIFT     =           73, // drift mode
    RC_AUX_SAILBOAT_MOTOR_3POS = 74, // Sailboat motoring 3pos
    RC_AUX_SURFACE_TRACKING =    75, // Surface tracking upwards or downwards
    RC_AUX_STANDBY  =            76, // Standby mode
    RC_AUX_TAKEOFF   =           77, // takeoff
    RC_AUX_RUNCAM_CONTROL =      78, // control RunCam device
    RC_AUX_RUNCAM_OSD_CONTROL =  79, // control RunCam OSD
    RC_AUX_VISODOM_ALIGN =       80, // align visual odometry camera's attitude to AHRS
    RC_AUX_DISARM =              81, // disarm vehicle
    RC_AUX_Q_ASSIST =            82, // disable, enable and force Q assist
    RC_AUX_ZIGZAG_Auto =         83, // zigzag auto switch
    RC_AUX_AIRMODE =             84, // enable / disable airmode for copter
    RC_AUX_GENERATOR   =         85, // generator control
    RC_AUX_TER_DISABLE =         86, // disable terrain following in CRUISE/FBWB modes
    RC_AUX_CROW_SELECT =         87, // select CROW mode for diff spoilers;high disables,mid forces progressive
    RC_AUX_SOARING =             88, // three-position switch to set soaring mode
    RC_AUX_LANDING_FLARE =       89, // force flare, throttle forced idle, pitch to LAND_PITCH_CD, tilts up
    RC_AUX_EKF_POS_SOURCE =      90, // change EKF position source between primary, secondary and tertiary sources
    RC_AUX_ARSPD_CALIBRATE=      91, // calibrate airspeed ratio 
    RC_AUX_FBWA =                92, // Fly-By-Wire-A
    RC_AUX_RELOCATE_MISSION =    93, // used in separate branch MISSION_RELATIVE
    RC_AUX_VTX_POWER =           94, // VTX power level
    RC_AUX_FBWA_TAILDRAGGER =    95, // enables FBWA taildragger takeoff mode. Once this feature is enabled it will stay enabled until the aircraft goes above TKOFF_TDRAG_SPD1 airspeed, changes mode, or the pitch goes above the initial pitch when this is engaged or goes below 0 pitch. When enabled the elevator will be forced to TKOFF_TDRAG_ELEV. This option allows for easier takeoffs on taildraggers in FBWA mode, and also makes it easier to test auto-takeoff steering handling in FBWA.
    RC_AUX_MODE_SWITCH_RESET =   96, // trigger re-reading of mode switch
    RC_AUX_WIND_VANE_DIR_OFSSET= 97, // flag for windvane direction offset input, used with windvane type 2
    RC_AUX_TRAINING            = 98, // mode training
    RC_AUX_AUTO_RTL =            99, // AUTO RTL via DO_LAND_START

    // entries from 100-150  are expected to be developer
    // options used for testing
    RC_AUX_KILL_IMU1 =          100, // disable first IMU (for IMU failure testing)
    RC_AUX_KILL_IMU2 =          101, // disable second IMU (for IMU failure testing)
    RC_AUX_CAM_MODE_TOGGLE =    102, // Momentary switch to cycle camera modes
    RC_AUX_EKF_LANE_SWITCH =    103, // trigger lane switch attempt
    RC_AUX_EKF_YAW_RESET =      104, // trigger yaw reset attempt
    RC_AUX_GPS_DISABLE_YAW =    105, // disable GPS yaw for testing
    RC_AUX_DISABLE_AIRSPEED_USE = 106, // equivalent to AIRSPEED_USE 0
    RC_AUX_FW_AUTOTUNE =          107, // fixed wing auto tune
    RC_AUX_QRTL =               108, // QRTL mode
    RC_AUX_CUSTOM_CONTROLLER =  109,
    // if you add something here, make sure to update the documentation of the parameter in RC_Channel.cpp!
    // also, if you add an option >255, you will need to fix duplicate_options_exist

    // options 150-199 continue user rc switch options
    RC_AUX_CRUISE =             150,  // CRUISE mode
    RC_AUX_TURTLE =             151,  // Turtle mode - flip over after crash
    RC_AUX_SIMPLE_HEADING_RESET = 152, // reset simple mode reference heading to current
    RC_AUX_ARMDISARM =          153, // arm or disarm vehicle
    RC_AUX_ARMDISARM_AIRMODE =  154, // arm or disarm vehicle enabling airmode
    RC_AUX_TRIM_TO_CURRENT_SERVO_RC = 155, // trim to current servo and RC
    RC_AUX_TORQEEDO_CLEAR_ERR = 156, // clear torqeedo error
    RC_AUX_EMERGENCY_LANDING_EN = 157, //Force long FS action to FBWA for landing out of range
    RC_AUX_OPTFLOW_CAL =        158, // optical flow calibration
    RC_AUX_FORCEFLYING =        159, // enable or disable land detection for GPS based manual modes preventing land detection and maintainting set_throttle_mix_max
    RC_AUX_WEATHER_VANE_ENABLE = 160, // enable/disable weathervaning
    RC_AUX_TURBINE_START =      161, // initialize turbine start sequence
    RC_AUX_FFT_NOTCH_TUNE =     162, // FFT notch tuning function
    RC_AUX_MOUNT_LOCK =         163, // Mount yaw lock vs follow
    RC_AUX_LOG_PAUSE =          164, // Pauses logging if under logging rate control
    RC_AUX_ARM_EMERGENCY_STOP = 165, // ARM on high, MOTOR_ESTOP on low
    RC_AUX_CAMERA_REC_VIDEO =   166, // start recording on high, stop recording on low
    RC_AUX_CAMERA_ZOOM =        167, // camera zoom high = zoom in, middle = hold, low = zoom out
    RC_AUX_CAMERA_MANUAL_FOCUS = 168,// camera manual focus.  high = long shot, middle = stop focus, low = close shot
    RC_AUX_CAMERA_AUTO_FOCUS =  169, // camera auto focus
    RC_AUX_QSTABILIZE =         170, // QuadPlane QStabilize mode
    RC_AUX_MAG_CAL =            171, // Calibrate compasses (disarmed only)
    RC_AUX_BATTERY_MPPT_ENABLE = 172,// Battery MPPT Power enable. high = ON, mid = auto (controlled by mppt/batt driver), low = OFF. This effects all MPPTs.
    RC_AUX_PLANE_AUTO_LANDING_ABORT = 173, // Abort Glide-slope or VTOL landing during payload place or do_land type mission items

    // inputs from 200 will eventually used to replace RCMAP
    RC_AUX_ROLL =               201, // roll input
    RC_AUX_PITCH =              202, // pitch input
    RC_AUX_THROTTLE =           203, // throttle pilot input
    RC_AUX_YAW =                204, // yaw pilot input
    RC_AUX_MAINSAIL =           207, // mainsail input
    RC_AUX_FLAP =               208, // flap input
    RC_AUX_FWD_THR =            209, // VTOL manual forward throttle
    RC_AUX_AIRBRAKE =           210, // manual airbrake control
    RC_AUX_WALKING_HEIGHT =     211, // walking robot height input
    RC_AUX_MOUNT1_ROLL =        212, // mount1 roll input
    RC_AUX_MOUNT1_PITCH =       213, // mount1 pitch input
    RC_AUX_MOUNT1_YAW =         214, // mount1 yaw input
    RC_AUX_MOUNT2_ROLL =        215, // mount2 roll input
    RC_AUX_MOUNT2_PITCH =       216, // mount3 pitch input
    RC_AUX_MOUNT2_YAW =         217, // mount4 yaw input

    // inputs 248-249 are reserved for the Skybrush fork at
    // https://github.com/skybrush-io/ardupilot

    // inputs for the use of onboard lua scripting
    RC_AUX_SCRIPTING_1 =        300,
    RC_AUX_SCRIPTING_2 =        301,
    RC_AUX_SCRIPTING_3 =        302,
    RC_AUX_SCRIPTING_4 =        303,
    RC_AUX_SCRIPTING_5 =        304,
    RC_AUX_SCRIPTING_6 =        305,
    RC_AUX_SCRIPTING_7 =        306,
    RC_AUX_SCRIPTING_8 =        307,

    // this must be higher than any aux function above
    RC_AUX_FUNCTION_MAX =   308,
};
typedef enum RC_AUX_FUNC rc_aux_func_t;

// auxiliary switch handling (n.b.: we store this as 2-bits!):
enum RcAuxSwitchPos {
    RC_AUX_SWITCH_LOW,       // indicates auxiliary switch is in the low position (pwm <1200)
    RC_AUX_SWITCH_MIDDLE,    // indicates auxiliary switch is in the middle position (pwm >1200, <1800)
    RC_AUX_SWITCH_HIGH       // indicates auxiliary switch is in the high position (pwm >1800)
};

enum RcAuxFuncTriggerSource {
    RC_FUNC_TRIGGER_SOURCE_INIT,
    RC_FUNC_TRIGGER_SOURCE_RC,
    RC_FUNC_TRIGGER_SOURCE_BUTTON,
    RC_FUNC_TRIGGER_SOURCE_MAVLINK,
    RC_FUNC_TRIGGER_SOURCE_MISSION,
    RC_FUNC_TRIGGER_SOURCE_SCRIPTING,
};

typedef int8_t modeswitch_pos_t;

typedef struct rc_channel *rc_channel_t;
typedef const struct rc_channel *rc_channel_const_t;
typedef struct rc_ops* rc_ops_t;

struct rc_channel {
    rc_ops_t       ops;

    // pwm is stored here
    int16_t        radio_in;

    // value generated from PWM normalised to configured scale
    int16_t        control_in;

    Param_int16    radio_min;
    Param_int16    radio_trim;
    Param_int16    radio_max;

    Param_int8     reversed;
    Param_int16    dead_zone;

    Param_int16    option; // e.g. activate EPM gripper / enable fence

    enum RcControlType  type_in;
    int16_t             high_in;

    // the input channel this corresponds to
    uint8_t     ch_in;

    // overrides
    uint16_t override_value;
    uint32_t last_override_time;

    // Structure used to detect and debounce switch changes
    struct {
        int8_t debounce_position;     //初始化为-1
        int8_t current_position;      //初始化为-1
        uint32_t last_edge_time_ms;
    } switch_state;
};

struct rc_ops {
    void (*init_aux_function)(rc_channel_t rc, rc_aux_func_t ch_option, enum RcAuxSwitchPos ch_flag);

    // virtual function to be overridden my subclasses
    bool (*do_aux_function)(rc_channel_t rc, rc_aux_func_t ch_option, enum RcAuxSwitchPos ch_flag);

    void (*mode_switch_changed)(rc_channel_t rc, modeswitch_pos_t new_pos);
};


enum RcsOption {
    RCS_IGNORE_RECEIVER         = (1U << 0), // RC receiver modules
    RCS_IGNORE_OVERRIDES        = (1U << 1), // MAVLink overrides
    RCS_IGNORE_FAILSAFE         = (1U << 2), // ignore RC failsafe bits
    RCS_FPORT_PAD               = (1U << 3), // pad fport telem output
    RCS_LOG_DATA                = (1U << 4), // log rc input bytes
    RCS_ARMING_CHECK_THROTTLE   = (1U << 5), // run an arming check for neutral throttle
    RCS_ARMING_SKIP_CHECK_RPY   = (1U << 6), // skip the an arming checks for the roll/pitch/yaw channels
    RCS_ALLOW_SWITCH_REV        = (1U << 7), // honor the reversed flag on switches
    RCS_CRSF_CUSTOM_TELEMETRY   = (1U << 8), // use passthrough data for crsf telemetry
    RCS_SUPPRESS_CRSF_MESSAGE   = (1U << 9), // suppress CRSF mode/rate message for ELRS systems
    RCS_MULTI_RECEIVER_SUPPORT  = (1U << 10), // allow multiple receivers
    RCS_USE_CRSF_LQ_AS_RSSI     = (1U << 11), // returns CRSF link quality as RSSI value, instead of RSSI
};

typedef struct rcs_channel* rcs_channel_t;
typedef struct rcs_ops* rcs_ops_t;

struct rcs_channel {
    rcs_ops_t      ops;

    rc_channel_t   channels[NUM_RC_CHANNELS];

    uint32_t last_update_ms;

    Param_float  _override_timeout;
    Param_int32  _options;
    Param_int32  _protocols;
    Param_float  _fs_timeout;

    bool has_new_overrides;
    bool _has_had_rc_receiver; // true if we have had a direct detach RC reciever, does not include overrides
    bool _has_had_override; // true if we have had an override on any channel

    // Allow override by default at start
    bool _gcs_overrides_enabled;    // default, true

    // true if GCS is performing a RC calibration
    bool gcs_is_calibrating;
};

struct rcs_ops {
    // this function is implemented in the child class in the vehicle
    // code
    rc_channel_t (*channel)(rcs_channel_t rcs, const uint8_t chan);

    void (*read_mode_switch)(rcs_channel_t rcs);

    // has_valid_input should be pure-virtual when Plane is converted
    bool (*has_valid_input)(rcs_channel_t rcs);

    rc_channel_t (*get_arming_channel)(rcs_channel_t rcs);

    bool (*arming_check_throttle)(rcs_channel_t rcs);

    // flight_mode_channel_number must be overridden in vehicle specific code
    int8_t (*flight_mode_channel_number)(rcs_channel_t rcs);
};

/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void        rc_ctor(rc_channel_t rc, rc_ops_t ops);

// read input from hal.rcin - create a control_in value
bool        rc_update(rc_channel_t rc);

// setup the control preferences
void        rc_set_range(rc_channel_t rc, uint16_t high);
static inline uint16_t    rc_get_range(rc_channel_const_t rc) { return rc->high_in; }
void        rc_set_angle(rc_channel_t rc, uint16_t angle);
bool        rc_get_reverse(rc_channel_t rc);
void        rc_set_default_dead_zone(rc_channel_t rc, int16_t dzone);
static inline uint16_t    rc_get_dead_zone(rc_channel_const_t rc) { return rc->dead_zone; }

// get the center stick position expressed as a control_in value
int16_t     rc_get_control_mid(rc_channel_t rc);

// calculate an angle given dead_zone and trim. This is used by the quadplane code
// for hover throttle
int16_t     rc_pwm_to_angle_dz_trim(rc_channel_t rc, uint16_t _dead_zone, uint16_t _trim);

// return a normalised input for a channel, in range -1 to 1,
// centered around the channel trim. Ignore deadzone.
float       rc_norm_input(rc_channel_t rc);

// return a normalised input for a channel, in range -1 to 1,
// centered around the channel trim. Take into account the deadzone
float       rc_norm_input_dz(rc_channel_t rc);

// return a normalised input for a channel, in range -1 to 1,
// ignores trim and deadzone
float       rc_norm_input_ignore_trim(rc_channel_t rc);

// returns true if input is within deadzone of min
static inline bool        rc_in_min_dz(rc_channel_t rc) { return rc->radio_in < rc->radio_min + rc->dead_zone; }

uint8_t     rc_percent_input(rc_channel_t rc);

/*
  return true if input is within deadzone of trim
*/
bool        rc_in_trim_dz(rc_channel_t rc);

static inline int16_t    rc_get_radio_in(rc_channel_const_t rc) { return rc->radio_in;}
static inline void       rc_set_radio_in(rc_channel_t rc, int16_t val) {rc->radio_in = val;}

static inline int16_t    rc_get_control_in(rc_channel_const_t rc) { return rc->control_in;}
static inline void       rc_set_control_in(rc_channel_t rc, int16_t val) { rc->control_in = val;}

void        rc_clear_override(rc_channel_t rc);
void        rc_set_override(rc_channel_t rc, const uint16_t v, const uint32_t timestamp_ms);
bool        rc_has_override(rc_channel_const_t rc);

/*
  perform stick mixing on one channel
  This type of stick mixing reduces the influence of the auto
  controller as it increases the influence of the users stick input,
  allowing the user full deflection if needed
 */
float       rc_stick_mixing(rc_channel_t rc, const float servo_in);

// get control input with zero deadzone
int16_t     rc_get_control_in_zero_dz(rc_channel_t rc);

/*
  convert a pulse width modulation value to a value in the configured
  range, using the specified deadzone
 */
int16_t     rc_pwm_to_range_dz(rc_channel_t rc, uint16_t _dead_zone);

/*
  return an "angle in centidegrees" (normally -4500 to 4500) from
  the current radio_in value using the specified dead_zone
 */
static inline int16_t     rc_pwm_to_angle_dz(rc_channel_t rc, uint16_t _dead_zone) { return rc_pwm_to_angle_dz_trim(rc, _dead_zone, rc->radio_trim); }

/*
  return an "angle in centidegrees" (normally -4500 to 4500) from
  the current radio_in value
 */
static inline int16_t     rc_pwm_to_angle(rc_channel_t rc) { return rc_pwm_to_angle_dz(rc, rc->dead_zone); }

/*
  convert a pulse width modulation value to a value in the configured
  range
 */
static inline int16_t     rc_pwm_to_range(rc_channel_t rc) { return rc_pwm_to_range_dz(rc, rc->dead_zone); }

static inline int16_t     rc_get_radio_min(rc_channel_const_t rc) {return rc->radio_min;}
static inline int16_t     rc_get_radio_max(rc_channel_const_t rc) {return rc->radio_max;}
static inline int16_t     rc_get_radio_trim(rc_channel_const_t rc) { return rc->radio_trim;}

static inline void        rc_set_and_save_trim(rc_channel_t rc) {
    param_t* radio_trim = param_get_by_variable2("RC", &rc->radio_trim);

    if (radio_trim != NULL) {
        param_set_obj(radio_trim, rc->radio_in);
    } else {
        rc->radio_trim = rc->radio_in;
    }
}

// set and save trim if changed
static inline void        rc_set_and_save_radio_trim(rc_channel_t rc, int16_t val) {
    param_t* radio_trim = param_get_by_variable2("RC", &rc->radio_trim);

    if (radio_trim != NULL) {
        param_set_obj(radio_trim, val);
    } else {
        rc->radio_trim = val;
    }
}

// check if any of the trim/min/max param are configured, this would indicate that the user has done a calibration at somepoint
static inline bool         rc_configured(rc_channel_t rc) {
    param_t* radio_min = param_get_by_variable2("RC", &rc->radio_min);
    param_t* radio_max = param_get_by_variable2("RC", &rc->radio_max);
    param_t* radio_trim = param_get_by_variable2("RC", &rc->radio_trim);

    return radio_min != NULL || radio_max != NULL || radio_trim != NULL;
}

static inline enum RcControlType  rc_get_type(rc_channel_t rc) { return rc->type_in; }

void        rc_reset_mode_switch(rc_channel_t rc);
void        rc_read_mode_switch(rc_channel_t rc);

// auxiliary switch support
void        rc_init_aux(rc_channel_t rc);
bool        rc_read_aux(rc_channel_t rc);

bool        rc_read_3pos_switch(rc_channel_const_t rc, enum RcAuxSwitchPos *ret);
bool        rc_read_6pos_switch(rc_channel_t rc, int8_t* position);

bool        rc_debounce_completed(rc_channel_t rc, int8_t position);

bool        rc_run_aux_function(rc_channel_t rc, rc_aux_func_t ch_option, enum RcAuxSwitchPos pos, enum RcAuxFuncTriggerSource source);

void        rc_InitAuxFunction(rc_channel_t rc, const rc_aux_func_t ch_option, const enum RcAuxSwitchPos ch_flag);
bool        rc_DoAuxFunction(rc_channel_t rc, const rc_aux_func_t ch_option, const enum RcAuxSwitchPos ch_flag);

void        rc_do_aux_function_clear_wp(const enum RcAuxSwitchPos ch_flag);
void        rc_do_aux_function_rc_override_enable(const enum RcAuxSwitchPos ch_flag);
void        rc_do_aux_function_mission_reset(const enum RcAuxSwitchPos ch_flag);
void        rc_do_aux_function_armdisarm(const enum RcAuxSwitchPos ch_flag);

enum RcAuxSwitchPos rc_get_aux_switch_pos(rc_channel_const_t rc);

static inline void rc_init_aux_function(rc_channel_t rc, rc_aux_func_t ch_option, enum RcAuxSwitchPos ch_flag) {
    if (rc->ops != NULL && rc->ops->init_aux_function != NULL) {
        rc->ops->init_aux_function(rc, ch_option, ch_flag);
        return;
    }

    rc_InitAuxFunction(rc, ch_option, ch_flag);
}

// virtual function to be overridden my subclasses
static inline bool rc_do_aux_function(rc_channel_t rc, rc_aux_func_t ch_option, enum RcAuxSwitchPos ch_flag) {
    if (rc->ops != NULL && rc->ops->do_aux_function != NULL) {
        return rc->ops->do_aux_function(rc, ch_option, ch_flag);
    }

    return rc_DoAuxFunction(rc, ch_option, ch_flag);
}

static inline void rc_mode_switch_changed(rc_channel_t rc, modeswitch_pos_t new_pos) {
    if (rc->ops != NULL && rc->ops->mode_switch_changed != NULL) {
        rc->ops->mode_switch_changed(rc, new_pos);
    }
}


/// RC_Channels
void rcs_ctor(rcs_ops_t ops);

// get singleton instance
rcs_channel_t rcs_get_singleton();

rc_channel_t rcs_chan(const uint8_t chan);

void rcs_init(void);
void rcs_init_aux_all();

// support for auxiliary switches:
// read_aux_switches - checks aux switch positions and invokes configured actions
void rcs_read_aux_all();

// compatability functions for Plane:
uint16_t rcs_get_radio_in(const uint8_t chan);
uint8_t rcs_get_radio_in2(uint16_t *chans, const uint8_t num_channels);

// update all the input channels
bool rcs_read_input(void);

uint8_t rcs_get_valid_channel_count(void);
int16_t rcs_get_receiver_rssi(void);
int16_t rcs_get_receiver_link_quality(void);

uint16_t rcs_get_override_mask(void);
bool rcs_gcs_overrides_enabled();
void rcs_set_gcs_overrides_enabled(bool enable);
void rcs_clear_overrides(void);
void rcs_set_override(const uint8_t chan, const int16_t value, const uint32_t timestamp_ms);
bool rcs_has_active_overrides();
void rcs_new_override_received();
bool rcs_get_override_timeout_ms(uint32_t *returned_timeout_ms);

// get failsafe timeout in milliseconds
uint32_t rcs_get_fs_timeout_ms();

// should a channel reverse option affect aux switches
bool rcs_switch_reverse_allowed(void);

enum RcAuxSwitchPos rcs_get_channel_pos(const uint8_t rcmapchan);
rc_channel_t rcs_find_channel_for_option(const rc_aux_func_t option);

// duplicate_options_exist - returns true if any options are duplicated
bool rcs_duplicate_options_exist();

// convert option parameter from old to new
void rcs_convert_options(const rc_aux_func_t old_option, const rc_aux_func_t new_option);

// should we ignore RC failsafe bits from receivers?
bool rcs_ignore_rc_failsafe(void);

bool rcs_ignore_overrides();
bool rcs_ignore_receiver();

bool rcs_arming_skip_checks_rpy();
bool rcs_suppress_crsf_message(void);
bool rcs_multiple_receiver_support();
bool rcs_use_crsf_lq_as_rssi(void);

// returns true if we have had a direct detach RC reciever, does not include overrides
bool rcs_has_had_rc_receiver();

// returns true if we have had an override on any channel
bool rcs_has_had_rc_override();

rc_channel_t rcs_flight_mode_channel();
void rcs_reset_mode_switch();

bool rcs_flight_mode_channel_conflicts_with_rc_option();

// return mask of enabled protocols.
uint32_t rcs_enabled_protocols();

uint32_t rcs_last_input_ms();

// method for other parts of the system (e.g. Button and mavlink)
// to trigger auxiliary functions
bool rcs_run_aux_function(rc_aux_func_t ch_option, enum RcAuxSwitchPos pos, enum RcAuxFuncTriggerSource source);

// set and get calibrating flag, stops arming if true
void rcs_set_calibrating(bool b);
bool rcs_get_calibrating();


/// ops
// this function is implemented in the child class in the vehicle
// code
rc_channel_t rcs_get_channel(const uint8_t chan);
void rcs_read_mode_switch();

// has_valid_input should be pure-virtual when Plane is converted
bool rcs_has_valid_input();

rc_channel_t rcs_get_arming_channel(void);
bool rcs_arming_check_throttle();

// flight_mode_channel_number must be overridden in vehicle specific code
int8_t rcs_flight_mode_channel_number();

bool rcs_ArmingCheckThrottle();
void rcs_ReadModeSwitch();

/*------------------------------------test------------------------------------*/

#ifdef __cplusplus
}
#endif



